package com.yumeng.framework.persistence.mybatis.base.page;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yumeng.common.auth.BaseAuthHelper;
import com.yumeng.common.auth.BaseAuthInfo;
import com.yumeng.common.buss.area.*;
import com.yumeng.common.buss.area.dto.AreaAndTimeDto;
import com.yumeng.common.buss.area.dto.AreaDto;
import com.yumeng.common.buss.area.dto.TopAreaAndTimeDto;
import com.yumeng.common.buss.area.dto.TopAreaDto;
import com.yumeng.common.data.annotations.DataScope;
import com.yumeng.common.data.annotations.DataScopeItem;
import com.yumeng.common.data.annotations.ParamOpt;
import com.yumeng.common.data.constants.UIConstants;
import com.yumeng.common.data.enums.ParamOptType;
import com.yumeng.common.exception.ScopePermException;
import com.yumeng.common.exception.YumengException;
import com.yumeng.common.helper.DateHelper;
import com.yumeng.common.helper.StringHelper;
import com.yumeng.common.utils.JacksonUtils;
import com.yumeng.common.utils.ReflectUtils;
import com.yumeng.config.helper.PropHelper;
import com.yumeng.config.properties.YumengMybatisPlusProperties;
import com.yumeng.framework.persistence.mybatis.base.dao.MpBaseDao;
import com.yumeng.framework.persistence.mybatis.base.page.param.ComplexParamItem;
import com.yumeng.framework.persistence.mybatis.base.page.param.PageParam;
import com.yumeng.framework.persistence.mybatis.base.page.param.temp.TempAreaParam;
import com.yumeng.framework.persistence.mybatis.base.page.param.temp.TempBussPermParam;
import com.yumeng.framework.persistence.mybatis.base.page.param.temp.TempNormalParam;
import com.yumeng.framework.persistence.mybatis.base.page.param.temp.TempTimeParam;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 *
 * @author wxd
 * @date 2025/10/10 17:33
 */
@Slf4j
@Accessors(chain = true)
public class MyPage<T> extends Page<T> {

    private static YumengMybatisPlusProperties mybatisPlusProperties;
    private static YumengMybatisPlusProperties getMybatisPlusProperties() {
        if (mybatisPlusProperties == null) {
            mybatisPlusProperties = PropHelper.getMybatisPlusProperties();
        }
        return mybatisPlusProperties;
    }

    private static final String ASC_FLAG = "asc";
    private static final String DESC_FLAG = "desc";


    /** 默认属性名-开始时间 */
    private static final String KSSJ_DEFAULT_PROP_NAME = "kssj";
    /** 默认属性名-结束时间 */
    private static final String JSSJ_DEFAULT_PROP_NAME = "jssj";

/*    public MyPage(long current, long size) {
        super(current, size);
    }*/

    private BaseAuthInfo authInfo;
    private MpBaseDao mpBaseDao;

    /** 是否需要匿名访问，开启后，部分验证就不那么严格了 */
    private boolean needAnon = false;
    /** 是否需要未知身份访问，开启后，部分验证就不那么严格了 */
    private boolean needUnknown = false;

    private MyPage(){};

    //此参数名用于使用“Xml文件”的分页方法，参数名【param】不能改变，与xml文件中 需一致
    private Map<String, Object> param;
    //此参数名用于使用“Mapper注入”的分页方法，selectPage
    private Map<String, ComplexParamItem> complexParam;

    private MyPage(Class<?> paramClass, PageParam pageParam, MpBaseDao mpBaseDao, PageMode pageMode) {
        super(pageParam.getPageNum(), pageParam.getPageSize());
        //设置排序信息
        setOrderParam(pageParam, pageMode, mpBaseDao.getKeyColumn());
        if (pageParam.getNeedAnon() != null){
            this.needAnon = pageParam.getNeedAnon();
        }
        if (pageParam.getNeedUnknown() != null){
            this.needUnknown = pageParam.getNeedUnknown();
        }
        this.mpBaseDao = mpBaseDao;
        this.authInfo = BaseAuthHelper.getCurrAuthInfo(false);//false，匿名访问列表 GuestAuthInfo
        //解析参数信息
        if (paramClass != null){
            setPageParam(paramClass, pageParam, pageMode);
        }else{
            log.info("MyPage分页参数为空");
        }
        setLogicDeleteParam(pageMode);
        if (PageMode.MAPPER.equals(pageMode)){
            log.debug("MyPage::complexParam:{}", JacksonUtils.toJsonString(complexParam));
        }else if (PageMode.XML.equals(pageMode)){
            log.debug("MyPage::param:{}", JacksonUtils.toJsonString(param));
        }
    }

    private MyPage(Map<String, Object> param, PageParam pageParam, MpBaseDao mpBaseDao){
        super(pageParam.getPageNum(), pageParam.getPageSize());
        //设置排序信息
        setOrderParam(pageParam, PageMode.XML, mpBaseDao.getKeyColumn());
        if (pageParam.getNeedAnon() != null){
            this.needAnon = pageParam.getNeedAnon();
        }
        if (pageParam.getNeedUnknown() != null){
            this.needUnknown = pageParam.getNeedUnknown();
        }
        if (MapUtils.isNotEmpty(param)){
            setSimplePageParamForXml(param);
        }else{
            log.info("MyPage分页参数为空(Map)");
        }
        log.debug("MyPage::param:{}", JacksonUtils.toJsonString(param));
    }

    /**
     * “Mapper注入”的分页方法 的参数生成
     * @param pageParam
     * @return
     */
    public static MyPage mapperBuild(PageParam pageParam, MpBaseDao mpBaseDao){
        return new MyPage<>(null, pageParam, mpBaseDao, PageMode.MAPPER);
    }
    /**
     * “Mapper注入”的分页方法 的参数生成
     * @param pageParam
     * @return
     */
    public static MyPage mapperBuild(Class<?> paramClass, PageParam pageParam, MpBaseDao mpBaseDao){
        return new MyPage<>(paramClass, pageParam, mpBaseDao, PageMode.MAPPER);
    }

    /**
     * “Xml文件”的分页方法 的参数生成
     * @param pageParam
     * @return
     */
    public static MyPage xmlBuild(PageParam pageParam, MpBaseDao mpBaseDao){
        return new MyPage<>(null, pageParam, mpBaseDao, PageMode.XML);
    }
    /**
     * “Xml文件”的分页方法 的参数生成
     * @param pageParam
     * @return
     */
    public static MyPage xmlBuild(Class<?> paramClass, PageParam pageParam, MpBaseDao mpBaseDao){
        return new MyPage<>(paramClass, pageParam, mpBaseDao, PageMode.XML);
    }

    /**
     * “Xml文件”的分页方法 的参数生成
     *  简单通过Map传参
     * @param pageParam
     * @return
     */
    public static MyPage xmlBuild(PageParam pageParam, Map<String, Object> param, MpBaseDao mpBaseDao){
        return new MyPage<>(param, pageParam, mpBaseDao);
    }

    public Map<String, Object> getParam() {
        return param;
    }

    public void setParam(Map<String, Object> param) {
        this.param = param;
    }

    public Map<String, ComplexParamItem> getComplexParam() {
        return complexParam;
    }

    public void setComplexParam(Map<String, ComplexParamItem> complexParam) {
        this.complexParam = complexParam;
    }

    /**
     * 设置排序参数
     * @param pageParam
     */
    private void setOrderParam(PageParam pageParam, PageMode pageMode, String keyColumn){
        if (StringUtils.isNotBlank(pageParam.getOrders())){
            List<OrderItem> items = new ArrayList<>();
            Map<String, String> orderNameMap = pageParam.orderNameMap();
            List<String> paramOrders = StringHelper.stringToList(pageParam.getOrders());
            if(MapUtils.isNotEmpty(orderNameMap)){
                paramOrders.forEach(p->{
                    String name = null;
                    boolean asc = true;
                    String[] orderDesc = p.split("_");
                    if (orderDesc.length == 2){
                        name = orderDesc[0];
                        if (DESC_FLAG.equalsIgnoreCase(orderDesc[1])){
                            asc = false;
                        }else if (ASC_FLAG.equalsIgnoreCase(orderDesc[1])){
                            asc = true;
                        }else{
                            throw new YumengException("排序信息异常：%s", p);
                        }
                    }else if (orderDesc.length == 1){
                        name = orderDesc[0];
                    }else{
                        throw new YumengException("排序信息异常：%s", p);
                    }
                    String colName = orderNameMap.get(name);
                    if (StringUtils.isNotBlank(colName)){
                        items.add(asc ? OrderItem.asc(colName) : OrderItem.desc(colName));
                    }else {
                        throw new YumengException("不支持当前排序字段：%s", name);
                    }
                });
            }
            super.addOrder(items);
        }else {
            //如果前端未指定排序，mapper模式需指定默认排序
            if (PageMode.MAPPER.equals(pageMode)){
                List<OrderItem> items = new ArrayList<>();
                if (CollectionUtils.isNotEmpty(pageParam.defaultOrders())){//优先级1
                    pageParam.defaultOrders().forEach(p->{
                        items.add(p.getAsc() ? OrderItem.asc(p.getName()) : OrderItem.desc(p.getName()));
                    });
                }else if (StringUtils.isNotBlank(pageParam.defaultColNamesDesc())){//优先级2
                    String[] array = StringHelper.stringToArray(pageParam.defaultColNamesDesc());
                    if (ArrayUtils.isNotEmpty(array)){
                        for (String col : array){
                            if (StringUtils.isNotBlank(col)){
                                items.add(OrderItem.desc(col));
                            }
                        }
                    }
                }else if (StringUtils.isNotBlank(pageParam.defaultColNamesAsc())){//优先级3
                    String[] array = StringHelper.stringToArray(pageParam.defaultColNamesAsc());
                    if (ArrayUtils.isNotEmpty(array)){
                        for (String col : array){
                            if (StringUtils.isNotBlank(col)){
                                items.add(OrderItem.asc(col));
                            }
                        }
                    }
                }else {//优先级4
                    if (StringUtils.isNotBlank(keyColumn)){
                        items.add(OrderItem.desc(keyColumn));
                    }
                }
                super.addOrder(items);
            }
        }
    }

    /**
     * 设置分页参数
     * @param paramClass
     * @param pageParam
     */
    private void setPageParam(Class<?> paramClass, PageParam pageParam, PageMode pageMode){
        if (PageMode.MAPPER.equals(pageMode)){
            //参数顺序与PageParam中一致，因此使用LinkedHashMap
            complexParam = new LinkedHashMap<>();
        }else if (PageMode.XML.equals(pageMode)){
            param = new HashMap<>();
        }
        Field[] fields = ReflectUtils.getDeclaredFields(paramClass);
        String currPropName = "";
        try{
            //当前地区编码参数
            TempAreaParam tempAreaParam = null;
            TempTimeParam tempTimeParam = null;
            List<TempNormalParam> tempParamList = new ArrayList<>();
            for (Field field : fields){
                String propName = field.getName();
                currPropName = propName;
                Class<?> typeClass = field.getType();
                Object val = field.get(pageParam);
                //忽略掉特殊值
                val = ignoreSpecialVal(val, typeClass);
                if (val != null){
                    String propNameLowerCase = propName.toLowerCase();
                    if (String.class.equals(typeClass)){
                        String valStr = val.toString();
                        //处理时间参数
                        if (isStartDateParam(propNameLowerCase)){
                            tempParamList.add(new TempNormalParam(propName, DateHelper.setStartTime(valStr),  field));
                        } else if (isEndDateParam(propNameLowerCase)){
                            tempParamList.add(new TempNormalParam(propName, DateHelper.setEndTime(valStr),  field));
                        } else if (isAreaCodeParam(propName)){
                            tempAreaParam = initAreaCodeParam(tempAreaParam, propName, valStr);
                        }else{
                            //普通String参数
                            tempParamList.add(new TempNormalParam(propName, valStr,  field));
                        }
                    }else if (TopAreaDto.class.equals(typeClass)){
                        tempAreaParam = initAreaCodeParam(tempAreaParam, (TopAreaDto)val);
                    }else if (TopAreaAndTimeDto.class.equals(typeClass)){
                        TopAreaAndTimeDto tempTimeDto = (TopAreaAndTimeDto)val;
                        tempAreaParam = initAreaCodeParam(tempAreaParam, tempTimeDto);

                        tempTimeParam = initTimeParam(tempTimeParam, tempTimeDto);
                        if (tempTimeParam != null){
                            tempTimeParam.setDefaultTimeColName(getDefaultTimeColName(field));
                        }
                    }else if (AreaDto.class.equals(typeClass)){
                        tempAreaParam = initAreaCodeParam(tempAreaParam, (AreaDto)val);
                    }else if (AreaAndTimeDto.class.equals(typeClass)){
                        AreaAndTimeDto tempTimeDto = (AreaAndTimeDto)val;
                        tempAreaParam = initAreaCodeParam(tempAreaParam, tempTimeDto);

                        tempTimeParam = initTimeParam(tempTimeParam, tempTimeDto);
                        if (tempTimeParam != null){
                            tempTimeParam.setDefaultTimeColName(getDefaultTimeColName(field));
                        }
                    }else if (Long.class.equals(typeClass) || Integer.class.equals(typeClass) || Double.class.equals(typeClass)){
                        tempParamList.add(new TempNormalParam(propName, val,  field));
                    }else{
                        throw new YumengException("不支持当前参数类型：%s", typeClass.getName());
                    }
                }
            }


            PageFilterMode pageFilterMode = Optional.ofNullable(pageParam.pageFilterMode()).orElse(PageFilterMode.AREA_OR_BUSS);//默认地区权限和业务权限只需按一种过滤
            TempBussPermParam bussPerm = checkBussPerm(paramClass);
            boolean filterByBuss = false;//按业务属性过滤（如企业用户，只能看本企业的数据）
            boolean filterByArea = false;//按地区属性过滤（如监管用户，只能看本地区的数据）
            if (bussPerm != null){
                filterByBuss = true;
            }
            if (PageFilterMode.AREA_OR_BUSS.equals(pageFilterMode)){//地区或业务
                if (!filterByBuss && tempAreaParam != null){
                    filterByArea = true;
                }
            }else if (PageFilterMode.AREA_AND_BUSS.equals(pageFilterMode)){//地区和业务
                if (tempAreaParam != null){
                    filterByArea = true;
                }
            }else{
                throw new YumengException("分页权限过滤模式异常");
            }

            //设置时间参数
            if (tempTimeParam != null){
                setParamForTempTimeParam(tempTimeParam, pageMode);
                tempTimeParam = null;
            }
            //设置业务过滤参数
            if (filterByBuss){
                setParam(bussPerm.getPermKey(), bussPerm.getPermObjVal(), ParamOptType.EQ, null, pageMode);
            }
            //设置地区参数
            String targetAreaCodeParam = null;//目标地区参数
            if (filterByArea){
                setParamForTempAreaParam(tempAreaParam, pageMode);
                targetAreaCodeParam = AreaHelper.getAreaCode(tempAreaParam.getAreaCodeProvince(), tempAreaParam.getAreaCodeCity(), tempAreaParam.getAreaCodeDistrict(), tempAreaParam.getAreaCodeSubdistrict(), tempAreaParam.getVillageCode());
                tempAreaParam = null;
            }
            //普通参数填充（时间、地区参数靠前）
            if (!tempParamList.isEmpty()){
                for (TempNormalParam tempParam : tempParamList){
                    if (filterByBuss && bussPerm.getPermKey().equals(tempParam.getPropName())){
                        //如果业务权限已过滤，则跳过该参数的填充
                        continue;
                    }
                    setParam(tempParam.getPropName(), tempParam.getValObj(), tempParam.getField(), pageMode);
                }
            }

            //地区权限验证（区分省级 和 顶级 平台）
            //如果按业务过滤，则不验证地区权限
            //如果没有按业务过滤，则必须按地区过滤，当没有地区没有参数时，认作最高地区权限
            if (!filterByBuss){
                if (StringUtils.isBlank(targetAreaCodeParam)){
                    targetAreaCodeParam = AreaHelper.getCurrSysAreaCode();
                }
                checkAreaPerm(targetAreaCodeParam, this.authInfo.getRegionCode(), AreaHelper.getCurrSysAreaLevel());
            }
        }catch (IllegalAccessException e) {
            throw new YumengException("参数（%s）解析获取值失败", currPropName);
        }
    }

    /**
     * 设置简单分页参数，用于xml
     * @param pageParam
     */
    private void setSimplePageParamForXml(Map<String, Object> pageParam){
        if (MapUtils.isEmpty(pageParam)){
            return;
        }
        this.param = new HashMap<>();
        for (Map.Entry<String, Object> entry : pageParam.entrySet()) {
            String propName = entry.getKey();
            Object val = entry.getValue();
            param.put(propName, val);
        }
    }

    /**
     * 检测业务权限
     * @param paramClass
     * @return
     */
    private TempBussPermParam checkBussPerm(Class<?> paramClass){
        //如果存在业务过滤，则屏蔽匿名访问（如需匿名访问，重新写个方法）
        if (!checkAuthInfo("业务权限异常")){
            return null;
        }

        DataScope scope = AnnotationUtils.getAnnotation(paramClass, DataScope.class);
        DataScopeItem[] scopeItems = null;
        if (scope == null){//存在DataScope注解时，就不单独判断DataScopeItem注解
            DataScopeItem scopeItem = AnnotationUtils.getAnnotation(paramClass, DataScopeItem.class);
            if (scopeItem != null){
                scopeItems = new DataScopeItem[]{scopeItem};
            }
        }else{
            scopeItems = scope.scopeItems();
        }
        if (scope != null || ArrayUtils.isNotEmpty(scopeItems)){
            List<TempBussPermParam> list = new ArrayList<>();
            Map<String, Object> claims = this.authInfo.getDataMap();
            if (scope != null){
                boolean needIgnore = this.authInfo.getSuperAdmin() && scope.ignoreSuperAdmin();
                if (!needIgnore){
                    String[] keys = scope.scopeKeys();
                    for (String permKey : keys){
                        if (StringUtils.isNotBlank(permKey)){
                            Object permVal = claims.get(permKey);
                            if (permVal != null){
                                list.add(new TempBussPermParam(permKey, permVal));
                            }
                        }
                    }
                }
            }
            if (ArrayUtils.isNotEmpty(scopeItems)){
                for (DataScopeItem item : scopeItems){
                    boolean needIgnore = this.authInfo.getSuperAdmin() && item.ignoreSuperAdmin();
                    if (!needIgnore){
                        if (StringUtils.isNotBlank(item.scopeKey())){
                            Object permVal = claims.get(item.scopeKey());
                            if (permVal != null){
                                list.add(new TempBussPermParam(StringUtils.isNotBlank(item.propName()) ? item.propName() : item.scopeKey(), permVal));
                            }
                        }
                    }
                }
            }
            if (list.size() == 1){
                return list.get(0);
            }else if (list.size() > 1){
                //比如：用户是患者又是医生，查询挂号记录时，不支持同时以患者和医生的角度去查。
                //即一次访问，用户只能是一种角色
                throw new YumengException("目前仅支持单维度业务范围过滤");
            }else {
                return null;
            }
        }else{
            return null;
        }
    }

    /**
     * 设置参数
     *
     * @param paramKey
     * @param val
     * @param field
     * @param pageMode
     */
    private void setParam(String paramKey, Object val, Field field, PageMode pageMode){
        ParamOptType optType = null;
        String colName = null;
        //仅MAPPER模式下 需要判断ParamOpt
        if (PageMode.MAPPER.equals(pageMode)){
            ParamOpt paramOpt = AnnotationUtils.getAnnotation(field, ParamOpt.class);
            if (paramOpt != null){
                optType = paramOpt.value();
                colName = paramOpt.colName();
            }else {
                //没有设置操作类型的，默认 相等
                optType = ParamOptType.EQ;
                colName = "";
            }
        }
        setParam(paramKey, val, optType, colName, pageMode);
    }

    private void setParam(String paramKey, Object val, ParamOptType optType, String colName, PageMode pageMode){
        if (ParamOptType.IGNORE.equals(optType)){
            return;
        }
        //如果colName未指定，分页查询时会动态根据mybatis-plus的属性名-列名映射，获取对应的列名
        if (PageMode.MAPPER.equals(pageMode)){
            complexParam.put(paramKey, new ComplexParamItem(val, optType, colName));
        }else if (PageMode.XML.equals(pageMode)){
            //不判断@ParamOpt注解，由xml指定
            param.put(paramKey, val);
        }
    }

    /**
     * 设置逻辑删除字段
     * @param pageMode
     */
    private void setLogicDeleteParam(PageMode pageMode){
        //如果是逻辑删除，且外部没有设置删除字段参数
        if (this.mpBaseDao.getHasLogicDelete() && !checkContains(getMybatisPlusProperties().getLogicDeleteProp(), pageMode)){
            setParam(getMybatisPlusProperties().getLogicDeleteProp(),  getMybatisPlusProperties().getUnLogicDeleteVal(), ParamOptType.EQ, getMybatisPlusProperties().getLogicDeleteField(), pageMode);
        }
    }

    /**
     * 判断参数是否存在
     * @param paramKey
     * @param pageMode
     * @return
     */
    private boolean checkContains(String paramKey, PageMode pageMode){
        if (PageMode.MAPPER.equals(pageMode)){
            return complexParam.containsKey(paramKey);
        }else if (PageMode.XML.equals(pageMode)){
            return param.containsKey(paramKey);
        }
        return false;
    }

    /**
     * 判断是否为 时间开始 参数
     * @param propNameLowerCase
     * @return
     */
    private boolean isStartDateParam(String propNameLowerCase){
        if (propNameLowerCase.startsWith("kssj")
                || propNameLowerCase.endsWith("kssj")
                || propNameLowerCase.startsWith("startdate")
                || propNameLowerCase.endsWith("startdate")
                || propNameLowerCase.startsWith("starttime")
                || propNameLowerCase.endsWith("starttime")
        ){
            return true;
        }
        return false;
    }

    /**
     * 判断是否为 时间结束 参数
     * @param propNameLowerCase
     * @return
     */
    private boolean isEndDateParam(String propNameLowerCase){
        if (propNameLowerCase.startsWith("jssj")
                || propNameLowerCase.endsWith("jssj")
                || propNameLowerCase.startsWith("enddate")
                || propNameLowerCase.endsWith("enddate")
                || propNameLowerCase.startsWith("endtime")
                || propNameLowerCase.endsWith("endtime")
        ){
            return true;
        }
        return false;
    }

    /**
     * 是否是地区参数
     * @param propName
     * @return
     */
    private boolean isAreaCodeParam(String propName){
        return AreaConstants.AREA_CODE_PROP_NAME.equals(propName)
                || AreaConstants.AREA_CODE_PROVINCE_PROP_NAME.equals(propName)
                || AreaConstants.AREA_CODE_CITY_PROP_NAME.equals(propName)
                || AreaConstants.AREA_CODE_DISTRICT_PROP_NAME.equals(propName)
                || AreaConstants.AREA_CODE_SUBDISTRICT_PROP_NAME.equals(propName)
                || AreaConstants.VILLAGE_CODE_PROP_NAME.equals(propName);
    }


    private TempAreaParam initAreaCodeParam(TempAreaParam param, AreaDto areaDto){
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_CITY_PROP_NAME, areaDto.getAreaCodeCity());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_DISTRICT_PROP_NAME, areaDto.getAreaCodeDistrict());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_SUBDISTRICT_PROP_NAME, areaDto.getAreaCodeSubdistrict());
        param = initAreaCodeParam(param, AreaConstants.VILLAGE_CODE_PROP_NAME, areaDto.getVillageCode());
        return param;
    }
    private TempAreaParam initAreaCodeParam(TempAreaParam param, AreaAndTimeDto areaDto){
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_CITY_PROP_NAME, areaDto.getAreaCodeCity());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_DISTRICT_PROP_NAME, areaDto.getAreaCodeDistrict());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_SUBDISTRICT_PROP_NAME, areaDto.getAreaCodeSubdistrict());
        param = initAreaCodeParam(param, AreaConstants.VILLAGE_CODE_PROP_NAME, areaDto.getVillageCode());
        return param;
    }
    private TempTimeParam initTimeParam(TempTimeParam param, AreaAndTimeDto areaDto){
        param = initTimeParam(param, KSSJ_DEFAULT_PROP_NAME, areaDto.getKssj());
        param = initTimeParam(param, JSSJ_DEFAULT_PROP_NAME, areaDto.getJssj());
        return param;
    }

    private TempAreaParam initAreaCodeParam(TempAreaParam param, TopAreaDto areaDto){
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_PROVINCE_PROP_NAME, areaDto.getAreaCodeProvince());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_CITY_PROP_NAME, areaDto.getAreaCodeCity());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_DISTRICT_PROP_NAME, areaDto.getAreaCodeDistrict());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_SUBDISTRICT_PROP_NAME, areaDto.getAreaCodeSubdistrict());
        param = initAreaCodeParam(param, AreaConstants.VILLAGE_CODE_PROP_NAME, areaDto.getVillageCode());
        return param;
    }
    private TempAreaParam initAreaCodeParam(TempAreaParam param, TopAreaAndTimeDto areaDto){
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_PROVINCE_PROP_NAME, areaDto.getAreaCodeProvince());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_CITY_PROP_NAME, areaDto.getAreaCodeCity());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_DISTRICT_PROP_NAME, areaDto.getAreaCodeDistrict());
        param = initAreaCodeParam(param, AreaConstants.AREA_CODE_SUBDISTRICT_PROP_NAME, areaDto.getAreaCodeSubdistrict());
        param = initAreaCodeParam(param, AreaConstants.VILLAGE_CODE_PROP_NAME, areaDto.getVillageCode());
        return param;
    }

    private TempTimeParam initTimeParam(TempTimeParam param, TopAreaAndTimeDto areaDto){
        param = initTimeParam(param, KSSJ_DEFAULT_PROP_NAME, areaDto.getKssj());
        param = initTimeParam(param, JSSJ_DEFAULT_PROP_NAME, areaDto.getJssj());
        return param;
    }

    /**
     * 地区参数初始化
     * @param param
     * @param propName
     * @param valStr
     */
    private TempAreaParam initAreaCodeParam(TempAreaParam param, String propName, String valStr){
        if (StringUtils.isBlank(valStr)){
            return param;
        }
        if (param == null){
            param = new TempAreaParam();
        }
        if (AreaConstants.AREA_CODE_PROP_NAME.equals(propName)){
            if (StringUtils.isNotBlank(param.getAreaCode()) && !param.getAreaCode().equals(valStr)){
                throw new YumengException("areaCode参数冲突");
            }
            param.setAreaCode(valStr);
        } else if (AreaConstants.AREA_CODE_PROVINCE_PROP_NAME.equals(propName)) {
            if (StringUtils.isNotBlank(param.getAreaCodeProvince()) && !param.getAreaCodeProvince().equals(valStr)){
                throw new YumengException("省级编码参数冲突");
            }
            param.setAreaCodeProvince(valStr);
        } else if (AreaConstants.AREA_CODE_CITY_PROP_NAME.equals(propName)) {
            if (StringUtils.isNotBlank(param.getAreaCodeCity()) && !param.getAreaCodeCity().equals(valStr)){
                throw new YumengException("市级编码参数冲突");
            }
            param.setAreaCodeCity(valStr);
        } else if (AreaConstants.AREA_CODE_DISTRICT_PROP_NAME.equals(propName)) {
            if (StringUtils.isNotBlank(param.getAreaCodeDistrict()) && !param.getAreaCodeDistrict().equals(valStr)){
                throw new YumengException("区级编码参数冲突");
            }
            param.setAreaCodeDistrict(valStr);
        } else if (AreaConstants.AREA_CODE_SUBDISTRICT_PROP_NAME.equals(propName)) {
            if (StringUtils.isNotBlank(param.getAreaCodeSubdistrict()) && !param.getAreaCodeSubdistrict().equals(valStr)){
                throw new YumengException("街道级编码参数冲突");
            }
            param.setAreaCodeSubdistrict(valStr);
        } else if (AreaConstants.VILLAGE_CODE_PROP_NAME.equals(propName)) {
            if (StringUtils.isNotBlank(param.getVillageCode()) && !param.getVillageCode().equals(valStr)){
                throw new YumengException("村级编码参数冲突");
            }
            param.setVillageCode(valStr);
        } else{
            throw new YumengException("地区参数属性异常");
        }
        return param;
    }

    /**
     * 时间参数初始化
     * @param param
     * @param propName
     * @param valStr
     */
    private TempTimeParam  initTimeParam(TempTimeParam param, String propName, String valStr){
        if (StringUtils.isBlank(valStr)){
            return param;
        }
        if (param == null){
            param = new TempTimeParam();
        }
        if (KSSJ_DEFAULT_PROP_NAME.equals(propName)) {
            if (StringUtils.isNotBlank(param.getKssj()) && !param.getKssj().equals(valStr)){
                throw new YumengException("开始时间参数冲突");
            }
            param.setKssj(DateHelper.setStartTime(valStr));
        }  else if (JSSJ_DEFAULT_PROP_NAME.equals(propName)) {
            if (StringUtils.isNotBlank(param.getJssj()) && !param.getJssj().equals(valStr)){
                throw new YumengException("结束时间参数冲突");
            }
            param.setJssj(DateHelper.setEndTime(valStr));
        } else{
            throw new YumengException("时间参数属性异常");
        }
        return param;
    }

    /**
     * 忽略特殊值
     * @param val
     * @return
     */
    private Object ignoreSpecialVal(Object val, Class<?> typeClass){
        //忽略掉一些特殊值
        if (val != null){
            boolean ignore = false;
            if (String.class.equals(typeClass)){
                //空字符串 “-1”字符串
                if (StringUtils.isBlank(val.toString())
                        || UIConstants.COMBBOX_TEMP_VALUE_STR.equals(val.toString())){
                    ignore = true;
                }
            }else if (Integer.class.equals(typeClass)){
                //整型-1
                if (UIConstants.COMBBOX_TEMP_VALUE_INT.equals(val)){
                    ignore = true;
                }
            }else if (Long.class.equals(typeClass)){
                //长整型-1
                if (UIConstants.COMBBOX_TEMP_VALUE_LONG.equals(val)){
                    ignore = true;
                }
            }
            if (ignore){
                val = null;
            }
        }
        return val;
    }

    /**
     * 设置地区参数
     * @param param
     */
    private void setParamForTempAreaParam(TempAreaParam param, PageMode pageMode){
        //优先areaCode，如果存在areaCode，会覆盖各级地区code
        if (StringUtils.isNotBlank(param.getAreaCode())){
            AreaCodeInfo codeInfo = AreaHelper.getAreaCodeInfo(param.getAreaCode());
            param.setAreaCodeProvince(codeInfo.getAreaCodeProvince());
            param.setAreaCodeCity(codeInfo.getAreaCodeCity());
            param.setAreaCodeDistrict(codeInfo.getAreaCodeDistrict());
            param.setAreaCodeSubdistrict(codeInfo.getAreaCodeSubdistrict());
            param.setVillageCode(codeInfo.getVillageCode());
        }
        if (AreaBussMode.SINGLE_CODE.equals(AreaHelper.getCurrAreaBussMode())){
            if (StringUtils.isNotBlank(param.getVillageCode())){
                setParam(AreaConstants.AREA_CODE_PROP_NAME, param.getVillageCode(), ParamOptType.EQ, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeSubdistrict())){
                setParam(AreaConstants.AREA_CODE_PROP_NAME, param.getAreaCodeSubdistrict(), ParamOptType.LIKE_RIGHT, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeDistrict())){
                setParam(AreaConstants.AREA_CODE_PROP_NAME, param.getAreaCodeDistrict(), ParamOptType.LIKE_RIGHT, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeCity())){
                setParam(AreaConstants.AREA_CODE_PROP_NAME, param.getAreaCodeCity(), ParamOptType.LIKE_RIGHT, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeProvince())){
                setParam(AreaConstants.AREA_CODE_PROP_NAME, param.getAreaCodeProvince(), ParamOptType.LIKE_RIGHT, null, pageMode);
            }
        } else if (AreaBussMode.LEVEL_CODE.equals(AreaHelper.getCurrAreaBussMode())) {
            if (StringUtils.isNotBlank(param.getVillageCode())){
                setParam(AreaConstants.VILLAGE_CODE_PROP_NAME, param.getVillageCode(), ParamOptType.EQ, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeSubdistrict())){
                setParam(AreaConstants.AREA_CODE_SUBDISTRICT_PROP_NAME, param.getAreaCodeSubdistrict(), ParamOptType.EQ, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeDistrict())){
                setParam(AreaConstants.AREA_CODE_DISTRICT_PROP_NAME, param.getAreaCodeDistrict(), ParamOptType.EQ, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeCity())){
                setParam(AreaConstants.AREA_CODE_CITY_PROP_NAME, param.getAreaCodeCity(), ParamOptType.EQ, null, pageMode);
            }else if (StringUtils.isNotBlank(param.getAreaCodeProvince())){
                setParam(AreaConstants.AREA_CODE_PROVINCE_PROP_NAME, param.getAreaCodeProvince(), ParamOptType.EQ, null, pageMode);
            }
        }else{
            throw new YumengException("当前地区业务模式未知：%", AreaHelper.getCurrAreaBussMode());
        }
    }

    /**
     * 设置时间参数
     * @param param
     */
    private void setParamForTempTimeParam(TempTimeParam param, PageMode pageMode){
        if (StringUtils.isNotBlank(param.getKssj())){
            setParam(KSSJ_DEFAULT_PROP_NAME, param.getKssj(), ParamOptType.GE, param.getDefaultTimeColName(), pageMode);
        }
        if (StringUtils.isNotBlank(param.getJssj())){
            setParam(JSSJ_DEFAULT_PROP_NAME, param.getJssj(), ParamOptType.LE, param.getDefaultTimeColName(), pageMode);
        }
    }

    /**
     * 验证地区权限
     * @param dtoAreaCode
     * @param permAreaCode
     * @param sysAreaLevel
     */
    private void checkAreaPerm(String dtoAreaCode, String permAreaCode, SysAreaLevel sysAreaLevel){
        //如果是匿名用户，则不验证地区权限
        if (!checkAuthInfo("无法判断地区权限")){
            return;
        }
        //如果当前是顶级地区系统
        if (SysAreaLevel.TOP.equals(sysAreaLevel)){
            if (AreaConstants.AREA_CODE_TOP.equals(permAreaCode)){
                //顶级权限，都能访问
                return;
            }
            //下面都为非顶级权限
            if (AreaConstants.AREA_CODE_TOP.equals(dtoAreaCode)){
                throw new ScopePermException("无顶级地区访问权限，权限[%s]-目标[%s]", permAreaCode, dtoAreaCode);
            }else{
                if (!dtoAreaCode.startsWith(permAreaCode)){
                    throw new ScopePermException("无地区访问权限，权限[%s]-目标[%s]", permAreaCode, dtoAreaCode);
                }
            }
        }else{
            if (AreaConstants.AREA_CODE_TOP.equals(dtoAreaCode) || AreaConstants.AREA_CODE_TOP.equals(permAreaCode)){
                throw new ScopePermException("非顶级地区系统，地区权限异常");
            }
            if (!dtoAreaCode.startsWith(permAreaCode)){
                throw new ScopePermException("无地区访问权限，权限[%s]-目标[%s]", permAreaCode, dtoAreaCode);
            }
        }
    }

    /**
     * 获取默认时间列名
     * @param field
     * @return
     */
    private String getDefaultTimeColName(Field field){
        ParamOpt paramOpt = AnnotationUtils.getAnnotation(field, ParamOpt.class);
        if (paramOpt != null){
            return paramOpt.defaultTimeColName();
        }else {
            return "";
        }
    }


    private boolean checkAuthInfo(String errMsg){
        if (this.authInfo.isAnon()){
            if (needAnon){
                return false;
            }else {
                throw new YumengException("当前为匿名用户-%s", errMsg);
            }
        }else if (this.authInfo.isUnKnown()){
            if (needUnknown){
                return false;
            }else {
                throw new YumengException("当前为未知用户-%s", errMsg);
            }
        }
        return true;
    }
}
